package com.zoweunion.mechanic.util.task;

import com.zoweunion.mechanic.controller.BaseController;
import com.zoweunion.mechanic.dao.TSSsVehicleModelDao;
import com.zoweunion.mechanic.dao.app.ToolCarDataMapper;
import com.zoweunion.mechanic.dao.base.BlackEnclosureDao;
import com.zoweunion.mechanic.dao.base.MessageRecordDao;
import com.zoweunion.mechanic.dao.base.OwnerDao;
import com.zoweunion.mechanic.plugins.redis.RedisUtil;
import com.zoweunion.mechanic.service.base.BaseService;
import com.zoweunion.mechanic.service.base.LockCarService;
import com.zoweunion.mechanic.util.DateUtil;
import com.zoweunion.mechanic.util.GPSUtil;
import com.zoweunion.mechanic.util.JiGuangPushUtil;
import com.zoweunion.mechanic.util.UuidUtil;
import com.zoweunion.mechanic.util.constants.PushConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.awt.geom.Point2D;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 车辆黑区电子围栏报警通知
 *
 * @author nh
 */
@Component
public class CarBlackEnclosurePushScheduleTask extends BaseController {

    private Logger logger = LoggerFactory.getLogger(CarEnclosurePushScheduleNewTask.class);

    private final String CAR_LOCATION_PREFIX_BLACK = "car_location_prefix_black:";

    private final String warning_enter_enclosure_area_black = "请注意，车辆(%s-%s-%s)已驶入黑区电子围栏区域！";
    private final String warning_pass_enclosure_area_black = "请注意，车辆(%s-%s-%s)通过黑区电子围栏区域！";

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private BaseService baseService;

    @Autowired
    private MessageRecordDao messageRecordDao;

    @Autowired
    private BlackEnclosureDao blackEnclosureDao;

    @Autowired
    private ToolCarDataMapper toolCarDataMapper;

    @Autowired
    private JiGuangPushUtil jiGuangPushUtil;

    @Autowired
    private LockCarService lockCarService;

    @Autowired
    private OwnerDao ownerDao;

    @Autowired
    private TSSsVehicleModelDao tsSsVehicleModelDao;

    private static double EARTH_RADIUS = 6378138.0;

    public static final long EXPIRE_TIME = 600;//上一次设置值为：3600

    /**
     * 每60秒执行一次
     */
    @Scheduled(cron = "*/60 * * * * ? ")
    private void task() {
        logBefore(logger, "开启黑区电子围栏预警定时任务");
        // 获取所有的车辆
        Map<String, Object> reqMap = new HashMap();
        reqMap.put("s_id","8f9c43506ec84c35a93e377f4ea0ee72");
        //reqMap.put("examine_status",1);
        //reqMap.put("hasLockCar",1);
        reqMap.put("currentPage",0);
        reqMap.put("pageSize",99999);
        List<Map<String, Object>> cars = ownerDao.getCarsByBankingIdPage(reqMap);
        for (Map<String, Object> map : cars) {
            String carId = map.get("c_id").toString();
            //拼接参数
            String thingsId = map.get("things_id").toString();
            String model = map.get("model") != null ? map.get("model").toString() : "";
            String brand = map.get("brand") != null ? map.get("brand").toString() : "";
            String car_type = map.get("car_type") != null ? map.get("car_type").toString() : "";
            Map<String, Object> pcCarMap = toolCarDataMapper.getPcCar(thingsId);
            if (pcCarMap == null || pcCarMap.get("device_template") == null) {
                continue;
            }

            // String tableName = ThingsData.getTableName(pcCarMap.get("device_template").toString());
            // 获取车辆最新设备信息
            // Map<String, Object> carDataMap = toolCarDataMapper.lastCarData(tableName, thingsId);

            Map<String, Object> paraMap = new HashMap<>();
            paraMap.put("things_id", thingsId);
            Map<String, Object> carDataMap = tsSsVehicleModelDao.mapInfoNew(paraMap);
            if (carDataMap == null) {
                continue;
            }
            // 经度
            String longitude = carDataMap.get("GPSlongitude").toString();
            // 纬度
            String latitude = carDataMap.get("GPSlatitude").toString();

            // 经纬度有效验证
            double[] doubles = GPSUtil.gps84_To_Gcj02(Double.valueOf(latitude), Double.valueOf(longitude));
            if (doubles.length >= 2) {
                if ((doubles[0] > 0.06 || doubles[0] < -0.06) && (doubles[1] > 0.06 || doubles[1] < -0.06)) {
                    double GPSlongitude = doubles[1];
                    double GPSlatitude = doubles[0];
                    String carKey = CAR_LOCATION_PREFIX_BLACK + carId;

                    // 判断redis里有没有此车辆上一次位置
                    if (!redisUtil.exists(carKey)) {
                        Map<String, Object> carLocationMap = new HashMap<>();
                        carLocationMap.put("GPSlongitude", GPSlongitude);
                        carLocationMap.put("GPSlatitude", GPSlatitude);
                        //将本次实时位置传入redis
                        redisUtil.set(carKey, carLocationMap, EXPIRE_TIME);
                        continue;
                    }

                    //取redis上一次位置数据
                    Map<String, Object> lngLatMap = (Map<String, Object>) redisUtil.get(carKey);

                    double lastGPSlongitude = Double.valueOf(lngLatMap.get("GPSlongitude").toString());
                    double lastGPSlatitude = Double.valueOf(lngLatMap.get("GPSlatitude").toString());

                    //获取金融平台下所有的黑区电子围栏
                    List<Map<String, Object>> enclosures = blackEnclosureDao.getAllBlackEnclosureBySid(reqMap);

                    if(enclosures.size() > 0) {
                        //根据电子围栏获取所有的经纬度
                        List<Map<String, Object>> enclosurePolygons = blackEnclosureDao.getAllBlackEnclosurePolygonByEId(enclosures);

                        for (Map<String, Object> enclosure : enclosures) {
                            int type = Integer.parseInt(enclosure.get("type").toString());
                            //获取黑区电子围栏绑定的通知人
                            List<Map<String, Object>> enclosureNotifiers = this.blackEnclosureDao.getBlackEnclosureNotifierByEId(enclosure);
                            //如果围栏为圆形
                            if (type == 1) {
                                // 判断此时位置在不在圆形内
                                boolean isInCircle = isInCircle(GPSlongitude, GPSlatitude,
                                        Double.valueOf(enclosure.get("lng").toString()), Double.valueOf(enclosure.get("lat").toString()),
                                        enclosure.get("radius").toString());
                                // 判断上一次位置在不在圆形内
                                boolean isLastInCircle = isInCircle(lastGPSlongitude, lastGPSlatitude,
                                        Double.valueOf(enclosure.get("lng").toString()), Double.valueOf(enclosure.get("lat").toString()),
                                        enclosure.get("radius").toString());

                                // 驶入报警
                                if (isInCircle && !isLastInCircle) {
                                    // 查询10分钟内是否推送过，若推送过，则10分钟内不再推送
                                    String context = "驶入";
                                    int count = toolCarDataMapper.selectPushCountInTenMinutes(enclosureNotifiers.get(0).get("user_id").toString(), 113, context); // 113: 黑区电子围栏报警
                                    if (count == 0) {
                                        if (Integer.parseInt(enclosure.get("entry_warning").toString()) == 0) {
                                            this.warning(enclosureNotifiers, enclosure, map.get("c_id").toString(), String.format(warning_enter_enclosure_area_black, model, brand, car_type));
                                        }
                                        if (enclosure.get("entry_lock_car") != null && Integer.valueOf(enclosure.get("entry_lock_car").toString()) == 1) {
                                            Map<String, Object> paramMap = new HashMap<>();
                                            paramMap.put("car_id", carId);
                                            paramMap.put("s_id", map.get("s_id").toString());
                                            paramMap.put("things_id", thingsId);
                                            this.lockCar(paramMap);
                                        }
                                    }
                                } else if(isInCircle && isLastInCircle) {
                                    // 查询10分钟内是否推送过，若推送过，则10分钟内不再推送
                                    String context = "驶入";
                                    int count = toolCarDataMapper.selectPushCountInTenMinutes(enclosureNotifiers.get(0).get("user_id").toString(), 113, context); // 113: 黑区电子围栏报警
                                    if (count == 0) {
                                        if (Integer.parseInt(enclosure.get("entry_warning").toString()) == 0) {
                                            this.warning(enclosureNotifiers, enclosure, map.get("c_id").toString(), String.format(warning_enter_enclosure_area_black, model, brand, car_type));
                                        }
                                        if (enclosure.get("entry_lock_car") != null && Integer.valueOf(enclosure.get("entry_lock_car").toString()) == 1) {
                                            Map<String, Object> paramMap = new HashMap<>();
                                            paramMap.put("car_id", carId);
                                            paramMap.put("s_id", map.get("s_id").toString());
                                            paramMap.put("things_id", thingsId);
                                            this.lockCar(paramMap);
                                        }
                                    }
                                }/*else if (!isInCircle && isLastInCircle) {
                                //查询10分钟内是否推送过，若推送过，则10分钟内不再推送
                                int count = toolCarDataMapper.selectPushCountInTenMinutes(enclosureNotifiers.get(0).get("user_id").toString(), 113); // 113: 黑区电子围栏报警
                                if(count == 0) {
                                    // 驶出报警
                                    if (Integer.parseInt(enclosure.get("out_warning").toString()) == 0) {
                                        this.warning(enclosureNotifiers, enclosure, map.get("c_id").toString(), warning_leave_enclosure_area);
                                    }
                                    // 驶出锁车
                                    if (enclosure.get("out_lock_car") != null && Integer.valueOf(enclosure.get("out_lock_car").toString()) == 1) {
                                        Map<String, Object> paramMap = new HashMap<>();
                                        paramMap.put("car_id", carId);
                                        paramMap.put("s_id", map.get("s_id").toString());
                                        paramMap.put("things_id", thingsId);
                                        this.outLockCar(paramMap);
                                    }
                                }
                            }*/
                            } else if (type == 2 || type == 3) {
                                List<Double> lon = new ArrayList<>();
                                List<Double> lat = new ArrayList<>();
                                for (Map<String, Object> enclosurePolygon : enclosurePolygons) {
                                    if (enclosure.get("id").toString().equals(enclosurePolygon.get("e_id").toString())) {
                                        lon.add(Double.valueOf(enclosurePolygon.get("lng").toString()));
                                        lat.add(Double.valueOf(enclosurePolygon.get("lat").toString()));
                                    }
                                }
                                Double[] lonArr = new Double[lon.size()];
                                lon.toArray(lonArr);
                                Double[] latArr = new Double[lat.size()];
                                lat.toArray(latArr);
                                //判断此时位置在不在多边形内
                                boolean isInPolygon = isInPolygon(GPSlongitude, GPSlatitude, lonArr, latArr);
                                //判断上一次位置在不在多边形内
                                boolean isLastInPolygon = isInPolygon(lastGPSlongitude, lastGPSlatitude, lonArr, latArr);
                                //驶入报警
                                if (isInPolygon && !isLastInPolygon) {
                                    String context = "驶入";
                                    int count = toolCarDataMapper.selectPushCountInTenMinutes(enclosureNotifiers.get(0).get("user_id").toString(), 113, context); // 113: 黑区电子围栏报警
                                    if (count == 0) {
                                        if (Integer.parseInt(enclosure.get("entry_warning").toString()) == 0) {
                                            this.warning(enclosureNotifiers, enclosure, map.get("c_id").toString(), String.format(warning_enter_enclosure_area_black, model,brand, car_type));
                                        }
                                        if (enclosure.get("entry_lock_car") != null && Integer.valueOf(enclosure.get("entry_lock_car").toString()) == 1) {
                                            Map<String, Object> paramMap = new HashMap<>();
                                            paramMap.put("car_id", carId);
                                            paramMap.put("s_id", map.get("s_id").toString());
                                            paramMap.put("things_id", thingsId);
                                            this.lockCar(paramMap);
                                        }
                                    }
                                }else if(isInPolygon && isLastInPolygon) {
                                    String context = "驶入";
                                    int count = toolCarDataMapper.selectPushCountInTenMinutes(enclosureNotifiers.get(0).get("user_id").toString(), 113, context); // 113: 黑区电子围栏报警
                                    if (count == 0) {
                                        if (Integer.parseInt(enclosure.get("entry_warning").toString()) == 0) {
                                            this.warning(enclosureNotifiers, enclosure, map.get("c_id").toString(), String.format(warning_enter_enclosure_area_black, model, brand, car_type));
                                        }
                                        if (enclosure.get("entry_lock_car") != null && Integer.valueOf(enclosure.get("entry_lock_car").toString()) == 1) {
                                            Map<String, Object> paramMap = new HashMap<>();
                                            paramMap.put("car_id", carId);
                                            paramMap.put("s_id", map.get("s_id").toString());
                                            paramMap.put("things_id", thingsId);
                                            this.lockCar(paramMap);
                                        }
                                    }
                                }/*else if (!isInPolygon && isLastInPolygon) {
                                //查询10分钟内是否推送过，若推送过，则10分钟内不再推送
                                String context = "驶出";
                                int count = toolCarDataMapper.selectPushCountInTenMinutes(enclosureNotifiers.get(0).get("user_id").toString(), 113, context); // 113: 黑区电子围栏报警
                                if(count == 0) {
                                    // 驶出报警
                                    if (Integer.parseInt(enclosure.get("out_warning").toString()) == 0) {
                                        this.warning(enclosureNotifiers, enclosure, map.get("c_id").toString(), warning_leave_enclosure_area);
                                    }
                                    // 驶出锁车
                                    if (enclosure.get("out_lock_car") != null && Integer.valueOf(enclosure.get("out_lock_car").toString()) == 1) {
                                        Map<String, Object> paramMap = new HashMap<>();
                                        paramMap.put("car_id", carId);
                                        paramMap.put("s_id", map.get("s_id").toString());
                                        paramMap.put("things_id", thingsId);
                                        this.lockCar(paramMap);
                                    }
                                }
                            }*/
                            } else if (type == 4) {
                                //保存此围栏的坐标
                                List<Map<String, Object>> enclosurePolygonNew = new ArrayList<>();
                                for (Map<String, Object> enclosurePolygon : enclosurePolygons) {
                                    if (enclosure.get("id").toString().equals(enclosurePolygon.get("e_id").toString())) {
                                        enclosurePolygonNew.add(enclosurePolygon);
                                    }
                                }
                                enclosurePolygonNew.sort((o1, o2) -> (Integer) o1.get("number") > (Integer) o2.get("number") ? 1 : (o1.get("number").equals(o2.get("number")) ? 0 : -1));
                                for (int i = 0; i < enclosurePolygonNew.size() - 1; i++) {
                                    boolean intersection = intersection(GPSlongitude, GPSlatitude, lastGPSlongitude, lastGPSlatitude,
                                            Double.valueOf(enclosurePolygonNew.get(i).get("lng").toString()), Double.valueOf(enclosurePolygonNew.get(i).get("lat").toString()),
                                            Double.valueOf(enclosurePolygonNew.get(i + 1).get("lng").toString()), Double.valueOf(enclosurePolygonNew.get(i + 1).get("lat").toString()));
                                    if (intersection && (Integer.parseInt(enclosure.get("entry_warning").toString()) == 0 || Integer.parseInt(enclosure.get("out_warning").toString()) == 0)) {
                                        // 驶入报警
                                        this.warning(enclosureNotifiers, enclosure, carId, String.format(warning_pass_enclosure_area_black, model, brand, car_type));
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        logAfter(logger);
    }

    private void lockCar(Map<String, Object> paramMap){
        paramMap.put("lock_user_id", "系统平台");
        paramMap.put("create_user", "系统平台");
        paramMap.put("update_user", "系统平台");
        paramMap.put("user_name", "系统平台");
        paramMap.put("isLocked", 1);
        paramMap.put("lock_type", 2);
        paramMap.put("smsStatus", 1);
        lockCarService.thingsIdCarOperation(paramMap);
    }

    private void warning(List<Map<String, Object>> enclosureNotifiers, Map<String, Object> enclosure, String cId, String message) {
        //驶入报警
        if (enclosureNotifiers.size() == 0) {
            return;
        }
        for (Map<String, Object> enclosureNotifier : enclosureNotifiers) {
            this.pushMessage(enclosure, enclosureNotifier, message, cId);
        }
    }

    private void pushMessage(Map<String, Object> enclosure, Map<String, Object> enclosureNotifier, String message, String cId) {
        // 推送的 map 封装
        String pushTime = DateUtil.formatTime(LocalDateTime.now());
        Map<String, Object> map = new HashMap<>();
        map.put("s_id", enclosure.get("s_id").toString());
        map.put("receive_user", enclosureNotifier.get("user_id").toString());
        map.put("client", "YD");
        map.put("id", UuidUtil.get32UUID());

        Map<String, Object> messageRecord = baseService.pushMessageEnclosureBlack(enclosureNotifier.get("user_id").toString(),
                pushTime, message, map);
        jiGuangPushUtil.publishMessagePush(messageRecord, PushConstants.KEY_JUMP_URL,"/message", map.get("id").toString());

        // 保存消息记录表
        map.put("context", message);
        map.put("user", enclosure.get("create_user").toString());
        map.put("send_type", 113); // 113: 黑区电子围栏报警
        map.put("jump_url", "/message");
        messageRecordDao.addMessageRecord(map);
    }

    private static double rad(double d) {
        return d * Math.PI / 180.0;
    }

    /**
     * 通过经纬度获取距离(单位：米)
     *
     * @param lat1
     * @param lng1
     * @param lat2
     * @param lng2
     * @return
     */
    public static double getDistance(double lat1, double lng1, double lat2,
                                     double lng2) {
        double radLat1 = rad(lat1);
        double radLat2 = rad(lat2);
        double a = radLat1 - radLat2;
        double b = rad(lng1) - rad(lng2);
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
                Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
        s = s * EARTH_RADIUS;
        s = Math.round(s * 10000) / 10000;
        return s;
    }

    /**
     * 判断一个点是否在圆形区域内
     */
    public static boolean isInCircle(double lng1, double lat1, double lng2, double lat2, String radius) {
        double distance = getDistance(lat1, lng1, lat2, lng2);
        double r = Double.parseDouble(radius);
        if (distance > r) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 判断是否在多边形区域内
     *
     * @param pointLon 要判断的点的纵坐标
     * @param pointLat 要判断的点的横坐标
     * @param lon      区域各顶点的纵坐标数组
     * @param lat      区域各顶点的横坐标数组
     * @return
     */
    public static boolean isInPolygon(double pointLon, double pointLat, Double[] lon,
                                      Double[] lat) {
        // 将要判断的横纵坐标组成一个点
        Point2D.Double point = new Point2D.Double(pointLon, pointLat);
        // 将区域各顶点的横纵坐标放到一个点集合里面
        List<Point2D.Double> pointList = new ArrayList<Point2D.Double>();
        double polygonPoint_x = 0.0, polygonPoint_y = 0.0;
        for (int i = 0; i < lon.length; i++) {
            polygonPoint_x = lon[i];
            polygonPoint_y = lat[i];
            Point2D.Double polygonPoint = new Point2D.Double(polygonPoint_x, polygonPoint_y);
            pointList.add(polygonPoint);
        }
        return isInPolygon(point, pointList);
    }

    /**
     * 判断点是否在多边形内
     *
     * @param point 测试点
     * @param pts   多边形的点
     * @return boolean
     * @throws
     * @Title: IsPointInPoly
     * @Description: TODO()
     */
    public static boolean isInPolygon(Point2D.Double point, List<Point2D.Double> pts) {

        int N = pts.size();
        boolean boundOrVertex = true;
        int intersectCount = 0;//交叉点数量
        double precision = 2e-10; //浮点类型计算时候与0比较时候的容差
        Point2D.Double p1, p2;//临近顶点
        Point2D.Double p = point; //当前点

        p1 = pts.get(0);
        for (int i = 1; i <= N; ++i) {
            if (p.equals(p1)) {
                return boundOrVertex;
            }

            p2 = pts.get(i % N);
            if (p.x < Math.min(p1.x, p2.x) || p.x > Math.max(p1.x, p2.x)) {
                p1 = p2;
                continue;
            }

            //射线穿过算法
            if (p.x > Math.min(p1.x, p2.x) && p.x < Math.max(p1.x, p2.x)) {
                if (p.y <= Math.max(p1.y, p2.y)) {
                    if (p1.x == p2.x && p.y >= Math.min(p1.y, p2.y)) {
                        return boundOrVertex;
                    }

                    if (p1.y == p2.y) {
                        if (p1.y == p.y) {
                            return boundOrVertex;
                        } else {
                            ++intersectCount;
                        }
                    } else {
                        double xinters = (p.x - p1.x) * (p2.y - p1.y) / (p2.x - p1.x) + p1.y;
                        if (Math.abs(p.y - xinters) < precision) {
                            return boundOrVertex;
                        }

                        if (p.y < xinters) {
                            ++intersectCount;
                        }
                    }
                }
            } else {
                if (p.x == p2.x && p.y <= p2.y) {
                    Point2D.Double p3 = pts.get((i + 1) % N);
                    if (p.x >= Math.min(p1.x, p3.x) && p.x <= Math.max(p1.x, p3.x)) {
                        ++intersectCount;
                    } else {
                        intersectCount += 2;
                    }
                }
            }
            p1 = p2;
        }
        if (intersectCount % 2 == 0) {//偶数在多边形外
            return false;
        } else { //奇数在多边形内
            return true;
        }
    }

    /**
     * 两【线段】是否相交
     *
     * @param l1x1 线段1的x1
     * @param l1y1 线段1的y1
     * @param l1x2 线段1的x2
     * @param l1y2 线段1的y2
     * @param l2x1 线段2的x1
     * @param l2y1 线段2的y1
     * @param l2x2 线段2的x2
     * @param l2y2 线段2的y2
     * @return 是否相交
     */
    public static boolean intersection(double l1x1, double l1y1, double l1x2, double l1y2,
                                       double l2x1, double l2y1, double l2x2, double l2y2) {
        // 快速排斥实验 首先判断两条线段在 x 以及 y 坐标的投影是否有重合。 有一个为真，则代表两线段必不可交。
        if (Math.max(l1x1, l1x2) < Math.min(l2x1, l2x2)
                || Math.max(l1y1, l1y2) < Math.min(l2y1, l2y2)
                || Math.max(l2x1, l2x2) < Math.min(l1x1, l1x2)
                || Math.max(l2y1, l2y2) < Math.min(l1y1, l1y2)) {
            return false;
        }
        // 跨立实验  如果相交则矢量叉积异号或为零，大于零则不相交
        if ((((l1x1 - l2x1) * (l2y2 - l2y1) - (l1y1 - l2y1) * (l2x2 - l2x1))
                * ((l1x2 - l2x1) * (l2y2 - l2y1) - (l1y2 - l2y1) * (l2x2 - l2x1))) > 0
                || (((l2x1 - l1x1) * (l1y2 - l1y1) - (l2y1 - l1y1) * (l1x2 - l1x1))
                * ((l2x2 - l1x1) * (l1y2 - l1y1) - (l2y2 - l1y1) * (l1x2 - l1x1))) > 0) {
            return false;
        }
        return true;
    }
}